#pragma once 

/*
    文件结构    compiler_server.cc    

                compile_run.hpp

            compile.hpp    +   runner.hpp
    客户端是封装好的 不对用户展现出来其他细节 所以整合的调用信息写在 compile_run.hpp
*/
#include "compiler.hpp"
#include "runner.hpp"
#include "../comm/log.hpp"
#include "../comm/util.hpp"
#include <signal.h>
#include <unistd.h>
//json/json.h

#include <json/json.h>


namespace ns_compile_and_run
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_compiler;
    using namespace ns_runner;
    class Compile_Run
    {
    public:
        //这个函数用于清理临时文件
        static void RemoveTempFile(const std::string &file_name)
        {
            //清理文件的个数是不确定的，但是有哪些我们是知道的
            std::string _src = PathUtil::Src(file_name);
            if(FileUtil::IsFileExists(_src)) unlink(_src.c_str());

            std::string _compiler_error = PathUtil::CompilerError(file_name);
            if(FileUtil::IsFileExists(_compiler_error)) unlink(_compiler_error.c_str());

            std::string _execute = PathUtil::Exe(file_name);
            if(FileUtil::IsFileExists(_execute)) unlink(_execute.c_str());

            std::string _stdin = PathUtil::Stdin(file_name);
            if(FileUtil::IsFileExists(_stdin)) unlink(_stdin.c_str());

            std::string _stdout = PathUtil::Stdout(file_name);
            if(FileUtil::IsFileExists(_stdout)) unlink(_stdout.c_str());
            
            std::string _stderr = PathUtil::Stderr(file_name);
            if(FileUtil::IsFileExists(_stderr)) unlink(_stderr.c_str());
        }
        //这个函数用于将错误码转换成字符串类型的 错误信息
        static std::string CodetoDesc(int code,const std::string& file_name)
        {
            std::string desc;
            switch(code)
            {
                case 0:
                desc="编译和运行成功";
                break;
                case -1:
                desc="提交代码为空";
                break;
                case -2:
                desc="未知错误";
                break;
                case -3:
                //编译时发生了错误 错误信息在 PathUtil::CompilerError(file_name) 里面
                FileUtil::ReadFile(PathUtil::CompilerError(file_name),&desc,true);
                break;
                case SIGABRT:
                //内存超过规定资源使用上限 发送的信号
                desc="内存超过范围";
                break;
                case SIGFPE:
                desc="浮点数溢出";
                break;
                case SIGXCPU:
                //24号信号 代表CPU使用超时
                desc="CPU使用超时";
                break;
                default:
                desc="未知: "+std::to_string(code);
                break;
            }
            return desc;
        }

        //编译并运行模块开始运行 首先一定会收到来自网络中的一个序列化数据 
        //该函数要做的就是将这个序列化数据进行反序列化 拿到用户提交的代码
        //然后复用 
        //Compiler::Compile(file_name)
        //Runner::Run(file_name, cpu_limit, mem_limit)

        /***************************************
        * 输入:
        * code： 用户提交的代码
        * input: 用户给自己提交的代码对应的输入，不做处理
        * cpu_limit: 时间要求
        * mem_limit: 空间要求
        * *
        输出:
        * 必填
        * status: 状态码
        * reason: 请求结果
        * 选填：
        * stdout: 我的程序运行完的结果
        * stderr: 我的程序运行完的错误结果
        * *
        参数：
        * in_json: {"code": "#include...", "input": "","cpu_limit":1, "mem_limit":10240}
        * out_json: {"status":"0", "reason":"","stdout":"","stderr":"",}
        * ************************************/
        static void start(const std::string &in_json, std::string *out_json)
        {
            Json::Value in_value;//把 in_json的数据反序列化到哪里

            //反序列化 开始
            Json::Reader _reader;
            _reader.parse(in_json,in_value);

            //反序列化结束 可以从 in_value拿协议好的数据了
            std::string code = in_value["code"].asString();
            std::string input = in_value["input"].asString();
            int cpu_limit=in_value["cpu_limit"].asInt();
            int mem_limit=in_value["mem_limit"].asInt();

            int status_code=0;//该变量用于记录错误码
            Json::Value out_value;//用于输出的结构体 输出时要将他序列化为 out_json
            int run_code=0;//该变量用于记录 Runner::Run()的返回结果 根据他来设置status_code
            std::string file_name;//先声明一下 因为在goto end 之间不能定义新的变量 file_name=FileUtil::UniqueFileName()
            /*
                status_code 
                == 0  运行成功 无错误
                >  1  运行时错误 错误信息为信号
                == -1 代码为空
                == -2 客户端内部错误 不对外展露 统一为未知错误
                == -3 编译错误 
            
            */

            if(code.size()==0)
            {
                status_code=-1;//代码为空 
                goto END;
            } 
            //走到这里我们已经拿到了用户上传的code 我们需要将他放入temp路径下 即写入到temp路径
            //首先必须的是生成一个不会重复的文件名
            file_name=FileUtil::UniqueFileName();
            std::cout << "file_name: " << file_name  <<std::endl;
            std::cout << "code: " << code << std::endl;
            //写入到temp路径下
            if(!FileUtil::WriteFile(PathUtil::Src(file_name),code))
            {
                status_code=-2;//客户端问题导致 没有写入文件 但是对外统称 未知错误
                goto END;
            }
            //编译
            if(!Compiler::Compile(file_name))
            {
                status_code=-3;//编译时错误
                goto END;
            }
            run_code=Runner::Run(file_name,cpu_limit,mem_limit);
            if(run_code<0)
            {
                //是内部问题 打开标准文件失败 或者 创建子进程失败 
                status_code=-2;
                goto END;
            }
            else if(run_code > 0)
            {
                //运行时问题 产生信号导致崩溃
                status_code=run_code;
            }
            else
            {
                //运行成功
                status_code=0;
            }
            END:
            //根据status_code填写 out_value
            out_value["status"]=status_code;
            out_value["reason"]=CodetoDesc(status_code,file_name);
            if(status_code==0)
            {
                std::string outstring;
                FileUtil::ReadFile(PathUtil::Stdout(file_name),&outstring,true);
                out_value["stdout"]=outstring;

                std::string errstring;
                FileUtil::ReadFile(PathUtil::Stderr(file_name),&errstring,true);
                out_value["stderr"]=errstring;
            }
            //将 out_value进行序列化
            Json::StyledWriter writer;
           
            *out_json=writer.write(out_value);
            //RemoveTempFile(file_name);
        }

    };

}
